/* eslint-disable no-console */
/* eslint-disable no-template-curly-in-string */
/* eslint-disable no-useless-concat */
/* eslint-disable prefer-destructuring */
import React, { useCallback, useEffect, useRef, useState } from 'react';

import { Controlled as CodeMirror } from 'react-codemirror2';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/eclipse.css';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/mode/javascript/javascript';
import SearchIcon from '@/assets/search_icon.png';
import { Button, Input, Tree } from 'antd';
import { CaretDownOutlined, DownOutlined, UpOutlined } from '@ant-design/icons';
import { FunctionMockData, ColumnsMockData } from './data';
import styles from './index.less';

require('codemirror/addon/hint/show-hint.js');
require('codemirror/addon/hint/sql-hint.js');

const Operations = ['+', '-', '*', '/', '(', ')'];

const { TreeNode } = Tree;

const Ex = 'abs(${dim_key}+${jy})';

interface Props {
  InFunctionData?: any; // 函数列表 格式：{ title: string; list: any[] }; title是上方显示的筛选条件名称
  InColumnData?: any; // 字段列表 格式：{ title: string; list: any[] }; title是上方显示的筛选条件名称
  callback?: any; // 回调函数
  expression?: string; // 待编辑的表达式
}

const ExpressionEditor: React.FC<Props> = props => {
  const { InFunctionData, InColumnData, callback, expression } = props;
  const sqlRef = useRef<any>(null);
  const editorRef = useRef<any>(null);
  const domRef = useRef<any>(null);
  const [value, setValue] = useState<string>('');
  const [functionData, setFunctionData] = useState<any>({});
  const [columnData, setColumnData] = useState<any>({});
  const [functionName, setFunctionName] = useState<any>(undefined); // 搜索函数名称
  const [functionTree, setFunctionTree] = useState<any[]>([]); // 函数树
  const [functionDict, setFunctionDict] = useState<any>({}); // 函数字典，用于选择函数时查找描述
  const [colName, setColName] = useState<string>(''); // 搜索字段名称
  const [colTree, setColTree] = useState<any[]>([]); // 字段树
  const [colDict, setColDict] = useState<any>({}); // 字段字典，用于选择字段时查找字段详细信息
  const [desList, setDesList] = useState<any[]>([]); // 函数描述列表
  const [isFold, setIsFold] = useState<boolean>(false); // 是否折叠函数说明
  const [funcSelectedKeys, setFuncSelectedKeys] = useState<any[]>([]); // 已选的函数key
  const [colSelectedKeys, setColSelectedKeys] = useState<any[]>([]); // 已选的字段key
  const [showEditor, setShowEditor] = useState<boolean>(false);
  const [hasChildren, setHasChildren] = useState<boolean>(false); // 字段数据是否有children，目前两种场景，一种场景是字段数据包含表，则有children；另一种是字段数据为衍生指标数据，没有children
  const [hasLoad, setHasLoad] = useState<boolean>(false); // 已经加载，加这边变量防止重复给编辑器赋值

  // 输入内容
  const changeValue = useCallback((val: string) => {
    setValue(val);
  }, []);


  useEffect(() => {
    if (InFunctionData) {
      setFunctionData(InFunctionData.list ? InFunctionData : FunctionMockData);
    } else {
      setFunctionData(FunctionMockData);
    }
  }, [InFunctionData]);

  useEffect(() => {
    if (InColumnData) {
      setColumnData(InColumnData.list ? InColumnData : ColumnsMockData);
    } else {
      setColumnData(ColumnsMockData);
    }
  }, [InColumnData]);

  useEffect(() => {
    if (
      (Object.keys(colDict).length > 0 ||
        Object.keys(functionDict).length > 0) &&
      !hasLoad
    ) {
      setHasLoad(true);
      const exp = expression ?? Ex;

      const expArr = exp
        .split(/(\+|-|\*|\/|\(|\))/g)
        .filter((e: any) => e !== ''); // 把字符串按照运算符分割成数组，并保留运算符
      let ch = 0; // 插入字符位置
      // console.log(expArr, '分割数组');
      const convertExp = (i: number) => {
        // 将待编辑的表达式转成编辑器显示的语句
        const c = expArr[i];
        if (c.indexOf('${') > -1) {
          // 检测到字段，根据字段key转成中文
          const col = c.replace('${', '').replace('}', '');

          const htmlnode = document.createElement('span');
          htmlnode.className = 'expression-marker';
          htmlnode.id = col; // 保存字段的key
          if (hasChildren) {
            htmlnode.setAttribute('data-table', colDict[col].tableName); // 保存字段的表名
          }
          const text = colDict[col]?.title;
          const textNode = document.createTextNode(text);
          htmlnode.appendChild(textNode);
          sqlRef.current.replaceRange(' ', { line: 0, ch });

          setTimeout(() => {
            // 设置文本标记
            sqlRef.current
              .getDoc()
              .markText(
                { line: 0, ch },
                { line: 0, ch: ch + 1 },
                { replacedWith: htmlnode, selectRight: true },
              );
            ch += 1;

            if (i < expArr.length - 1) {
              convertExp(i + 1);
            } else {
              setShowEditor(true);
            }
          }, 1);
        } else if (functionDict[c]) {
          const tmp = functionDict[c];
          if (!desList.some((item: any) => item.name === tmp.title)) {
            // 添加函数描述
            setDesList([
              ...desList,
              { name: tmp.title, description: tmp.description },
            ]);
          }
          const htmlnode = document.createElement('span');
          htmlnode.className = 'expression-key';
          const text = c;
          const textNode = document.createTextNode(text);
          htmlnode.appendChild(textNode);

          sqlRef.current.replaceRange(' ', { line: 0, ch });

          setTimeout(() => {
            // 设置文本标记
            sqlRef.current
              .getDoc()
              .markText(
                { line: 0, ch },
                { line: 0, ch: ch + 1 },
                { replacedWith: htmlnode, selectRight: true },
              );

            ch += 1;
            if (i < expArr.length - 1) {
              convertExp(i + 1);
            } else {
              setShowEditor(true);
            }
          }, 1);
        } else {
          // 插入文本
          sqlRef.current.replaceRange(c, { line: 0, ch });
          ch += c.length;
          if (i < expArr.length - 1) {
            convertExp(i + 1);
          } else {
            setShowEditor(true);
          }
        }
      };

      if (expArr.length > 0) {
        convertExp(0);
      } else {
        setShowEditor(true);
      }
    }
  }, [expression, colDict, functionDict, hasChildren, hasLoad]);

  useEffect(() => {
    if (showEditor) {
      setTimeout(() => {
        sqlRef.current.refresh();
      }, 200);
    }
  }, [showEditor]);

  useEffect(() => {
    if (!functionData.list) return;
    const List = functionData.list;
    if (!functionName) {
      setFunctionTree(List);
      const obj = Object.create(null);
      List.forEach((item: any) => {
        item.children.forEach((c: any) => {
          obj[c.key] = c;
        });
      });
      setFunctionDict(obj);
    } else if (
      !List.some(
        (item: any) =>
          item.title.toLowerCase().indexOf(functionName.toLocaleLowerCase()) >
          -1,
      )
    ) {
      const arr = List.map((f: any) => ({
        ...f,
        children: f.children.filter(
          (c: any) =>
            c.title.toLowerCase().indexOf(functionName.toLocaleLowerCase()) >
            -1,
        ),
      }));
      setFunctionTree(arr.filter((item: any) => item.children.length > 0));
    } else {
      setFunctionTree(
        List.filter(
          (item: any) =>
            item.title.toLowerCase().indexOf(functionName.toLocaleLowerCase()) >
            -1,
        ),
      );
    }
  }, [functionName, functionData]);

  useEffect(() => {
    if (!columnData.list) return;
    const List = columnData.list;
    if (!colName) {
      setColTree(List);
      const obj = Object.create(null);
      List.forEach((item: any) => {
        if (item.children) {
          item.children.forEach((c: any) => {
            obj[c.key] = c;
          });
        } else {
          obj[item.key] = item;
        }
      });
      setColDict(obj);
    } else if (
        columnData.list &&
        columnData.list.length > 0 &&
        columnData.list[0].children
      ) {
        // setHasChildren(true);
        setColTree(
          List.map((item: any) => ({
            ...item,
            children: item.children
              ? item.children.filter(
                  (c: any) =>
                    c.title.toLowerCase().indexOf(colName.toLowerCase()) > -1,
                )
              : undefined,
          })),
        );
      } else {
        setColTree(
          List.filter(
            (item: any) =>
              item.title.toLowerCase().indexOf(colName.toLowerCase()) > -1,
          ),
        );
      }
  }, [colName, columnData]);

  useEffect(() => {
    if (!editorRef.current) {
      editorRef.current = {};
    }
    editorRef.current.desList = [...desList];
  }, [desList]);

  const renderTreeNodes = (data: any[]) => {
    return data.map((item: any) => {
      if (item.children) {
        return (
          <TreeNode key={item.key} title={item.title} selectable={false}>
            {renderTreeNodes(item.children)}
          </TreeNode>
        );
      }
      return <TreeNode key={item.key} title={item.title} />;
    });
  };

  // 展开/收起函数说明
  const clickFold = useCallback(() => {
    setIsFold(!isFold);
  }, [isFold]);

  // 选择函数
  const selectFunction = useCallback(
    (keys: any[], e: any) => {
      editorRef.current.isOp = true;
      const { node } = e;
      // console.log('node:', node);
      if (!funcSelectedKeys.includes(node.key)) {
        setFuncSelectedKeys([node.key]);
      }
      const tmp = functionDict[node.key];
      if (!desList.some((item: any) => item.name === tmp.title)) {
        // 添加函数描述
        setDesList([
          ...desList,
          { name: tmp.title, description: tmp.description },
        ]);
      }
      setHasChildren(true);
      // 创建dom节点
      const htmlnode = document.createElement('span');
      htmlnode.className = 'expression-key';
      const text = node.title.split('(')[0];
      const textNode = document.createTextNode(text);
      htmlnode.appendChild(textNode);
      if (!editorRef.current.cursor) {
        // 先插入文本，然后用文本标记覆盖
        sqlRef.current.replaceRange(' ', { line: 0, ch: 0 });

        sqlRef.current.focus();
        setTimeout(() => {
          sqlRef.current
            .getDoc()
            .markText(
              { line: 0, ch: 0 },
              { line: 0, ch: 1 },
              { replacedWith: htmlnode, selectRight: true },
            );
          sqlRef.current.replaceRange('()', { line: 0, ch: 1 });
          sqlRef.current.getDoc().setCursor({
            line: 0,
            ch: 2,
          });
        }, 0);
      } else {
        sqlRef.current.replaceRange(' ', { ...editorRef.current.cursor });
        sqlRef.current.focus();
        setTimeout(() => {
          // 插入函数文本
          sqlRef.current.getDoc().markText(
            {
              line: editorRef.current.cursor.line,
              ch: editorRef.current.cursor.ch - 1,
            },
            { ...editorRef.current.cursor },
            { replacedWith: htmlnode, selectRight: true },
          );
          // 插入函数的括号，并设置光标位于括号中间
          sqlRef.current.replaceRange('(', { ...editorRef.current.cursor });
          sqlRef.current.replaceRange(')', {
            line: editorRef.current.cursor.line,
            ch: editorRef.current.cursor.ch + 1,
          });
          sqlRef.current.getDoc().setCursor({
            line: editorRef.current.cursor.line,
            ch: editorRef.current.cursor.ch,
          });
        }, 0);
      }
    },
    [funcSelectedKeys, functionDict, desList],
  );

  // 选择字段
  const selectCol = useCallback(
    (keys: any[], e: any) => {
      editorRef.current.isOp = false;
      const { node } = e;
      if (!colSelectedKeys.includes(node.key)) {
        setColSelectedKeys([node.key]);
      }
      const htmlnode = document.createElement('span');
      htmlnode.className = 'expression-marker';
      htmlnode.id = node.key;
      if (hasChildren) {
        htmlnode.setAttribute('data-table', colDict[node.key].tableName);
      }
      const text = node.title;
      const textNode = document.createTextNode(text);
      htmlnode.appendChild(textNode);
      if (!editorRef.current.cursor) {
        sqlRef.current.replaceRange(' ', { line: 0, ch: 0 });

        sqlRef.current.focus();
        setTimeout(() => {
          sqlRef.current
            .getDoc()
            .markText(
              { line: 0, ch: 0 },
              { line: 0, ch: 1 },
              { replacedWith: htmlnode, selectRight: true },
            );
          sqlRef.current.getDoc().setCursor({
            line: 0,
            ch: 1,
          });
        }, 0);
      } else {
        sqlRef.current.replaceRange(' ', { ...editorRef.current.cursor });
        sqlRef.current.focus();
        setTimeout(() => {
          sqlRef.current.getDoc().markText(
            {
              line: editorRef.current.cursor.line,
              ch: editorRef.current.cursor.ch - 1,
            },
            { ...editorRef.current.cursor },
            { replacedWith: htmlnode, selectRight: true },
          );
          sqlRef.current.getDoc().setCursor({
            ...editorRef.current.cursor,
          });
        }, 0);
      }
    },
    [colSelectedKeys, colDict, hasChildren],
  );

  // 选择运算符
  const selectOperator = useCallback((val: string) => {
    if (editorRef.current.cursor) {
      sqlRef.current.replaceRange(val, { ...editorRef.current.cursor });
      sqlRef.current.focus();
      setTimeout(() => {
        sqlRef.current.getDoc().setCursor({
          line: editorRef.current.cursor.line,
          ch: editorRef.current.cursor.ch,
        });
      }, 0);
    } else {
      sqlRef.current.replaceRange(val, { line: 0, ch: 0 });
      sqlRef.current.focus();
      setTimeout(() => {
        sqlRef.current.getDoc().setCursor({
          line: 0,
          ch: 1,
        });
      }, 0);
    }
  }, []);

  // 编辑器dom发生改变
  const changeEditor = () => {
    // console.log(domRef.current.ref.querySelector('.CodeMirror-line'));
    const line: any = domRef.current.ref.querySelector('.CodeMirror-line');
    // const line: any = document.querySelector('.CodeMirror-line');
    const lines = line.children[0];
    let showValue = ''; // 显示的表达式，字段为中文
    let enValue = ''; // 英文表达式，字段为英文
    const tobeKeys: any[] = []; // 待传keys
    if (lines.children) {
      const arr: any[] = Array.from(lines.children);
      arr.forEach((l: any) => {
        if (l.innerText === '&ZeroWidthSpace;') {
          return;
        }
        if (l.className === 'CodeMirror-widget' && l.children.length) {
          if (l.children[0].id) {
            enValue += '${' + `${l.children[0].id}` + '}';
            tobeKeys.push(`${l.children[0].id}`);
            if (hasChildren) {
              showValue += `${l.children[0].dataset.table}.${l.children[0].innerText}`;
            } else {
              showValue += `${l.children[0].innerText}`;
            }
          } else {
            enValue += l.children[0].innerText;
            showValue += l.children[0].innerText;
          }
        } else {
          showValue += l.innerText;
          enValue += l.innerText;
        }
      });
    }
    // console.log('showValue:', showValue);
    // console.log('enValue:', enValue);

    if (callback) {
      callback(showValue, enValue, tobeKeys);
    }
  };

  return (
    <>
      <div className={styles.editor}>
        <div className={styles.menuContainer}>
          <div className={styles.title}>函数</div>
          <div className={styles.box}>
            <Input
              allowClear
              placeholder="输入函数"
              onChange={(e: any) => setFunctionName(e.target.value)}
              suffix={<img src={SearchIcon} alt="搜索" style={{ width: 18, cursor: 'pointer' }} />}
            />
            <div className={styles.treeContainer}>
              {functionTree.length > 0 && (
                <Tree
                  showLine={{ showLeafIcon: false }}
                  defaultExpandAll={functionName !== undefined}
                  blockNode
                  switcherIcon={<CaretDownOutlined />}
                  onSelect={selectFunction}
                  selectedKeys={funcSelectedKeys}
                >
                  {renderTreeNodes(functionTree)}
                </Tree>
              )}
            </div>
          </div>
        </div>
        <div className={styles.menuContainer}>
          <div className={styles.title}>字段</div>
          <div className={styles.box}>
            <Input
              allowClear
              placeholder="输入字段"
              onChange={(e: any) => setColName(e.target.value)}
              suffix={<img src={SearchIcon} alt="搜索" style={{ width: 18, cursor: 'pointer' }} />}
            />
            <div className={styles.treeContainer}>
              {colTree.length > 0 && (
                <Tree
                  showLine={{ showLeafIcon: false }}
                  defaultExpandAll
                  blockNode
                  switcherIcon={<CaretDownOutlined />}
                  onSelect={selectCol}
                  selectedKeys={colSelectedKeys}
                >
                  {renderTreeNodes(colTree)}
                </Tree>
              )}
            </div>
          </div>
        </div>
        <div className={styles.menuContainer}>
          <div className={styles.title}>表达式</div>
          <div className={styles.box}>
            <div className={styles.operations}>
              {Operations.map((item: any) => (
                <Button key={item} onClick={() => selectOperator(item)}>
                  {item}
                </Button>
              ))}
            </div>
            <div className={styles.sqlEditor} style={{ height: isFold ? 'calc(100% - 84px)' : '' }}>
              <CodeMirror
                value={value}
                options={{
                  theme: 'eclipse',
                  lineNumbers: false,
                  lineWrapping: true,
                  mode: { name: 'text/x-mysql' },
                  extraKeys: { Tab: 'autocomplete' }, // 自动提示配置
                }}
                ref={domRef}
                editorDidMount={(editor) => {
                  sqlRef.current = editor;
                  editorRef.current = {};

                  // 注册回退键，覆盖原有事件，对删除做特殊处理
                  sqlRef.current.addKeyMap({
                    Backspace: (cm: any) => {
                      const Doc = cm;
                      const cursor = cm.getCursor();
                      const mark = Doc.findMarksAt(cursor);
                      let rangeMarks = [];
                      if (editorRef.current.ranges) {
                        // 用户选区
                        const { anchor, head } = editorRef.current.ranges;
                        rangeMarks = Doc.findMarks(
                          { line: head.line, ch: head.ch },
                          { line: anchor.line, ch: anchor.ch },
                        );
                      }
                      if (mark.length > 0 && (rangeMarks.length === 0 || rangeMarks.length === 1)) {
                        mark[mark.length - 1].clear();
                        const marks = Doc.getAllMarks();
                        if (
                          !editorRef.current.marks ||
                          editorRef.current.marks.length !== marks.length
                        ) {
                          editorRef.current.marks = [...marks];
                          const markNames = marks.map((item: any) => item.replacedWith.innerText);
                          setDesList(
                            editorRef.current.desList.filter((item: any) =>
                              markNames.includes(item.name.replace(/\((\S*)\)$/, '')),
                            ),
                          );
                        }
                        Doc.execCommand('delCharBefore');
                      } else {
                        Doc.execCommand('delCharBefore');
                      }
                    },
                  });

                  sqlRef.current.on('cursorActivity', (instance: any) => {
                    const Doc = instance.getDoc();
                    const cursor = Doc.getCursor();
                    if (!cursor.line && !cursor.ch) {
                      Reflect.deleteProperty(editorRef.current, 'cursor');
                    } else {
                      editorRef.current.cursor = { ...cursor };
                    }
                  });

                  sqlRef.current.on('beforeSelectionChange', (instance: any, obj: any) => {
                    const { ranges } = obj;
                    editorRef.current.ranges = ranges[0];
                  });

                  sqlRef.current.on('update', () => {
                    changeEditor();
                  });
                }}
                onBeforeChange={(editor, _data, val) => changeValue(val)}
              />
            </div>
            <div className={styles.desBox}>
              <div className={styles.desTitle}>
                函数说明
                {isFold ? (
                  <UpOutlined style={{ cursor: 'pointer' }} onClick={clickFold} />
                ) : (
                  <DownOutlined style={{ cursor: 'pointer' }} onClick={clickFold} />
                )}
              </div>
              {!isFold && (
                <div className={styles.desList}>
                  {desList.map((item: any) => (
                    <div className={styles.desItem} key={item.name}>
                      <p>{item.name}</p>
                      <p>{item.description}</p>
                    </div>
                  ))}
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </>
  );
};


export default ExpressionEditor;